package de.laures.cewolf.style;

import java.awt.BasicStroke;
import java.awt.Color;
import java.util.List;

import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickMarkPosition;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PieLabelLinkStyle;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.chart.renderer.category.StandardBarPainter;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYAreaRenderer;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYBubbleRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;

import de.laures.cewolf.taglib.AxisConstants;
import de.laures.cewolf.taglib.ChartConstants;
import de.laures.cewolf.taglib.ChartTypes;
import de.laures.cewolf.taglib.LayoutConstants;
import de.laures.cewolf.taglib.UnsupportedChartTypeException;
import de.laures.cewolf.util.ChartUtils;
import de.laures.cewolf.util.PropReader;

public class StyleFactory implements ChartConstants, AxisConstants, LayoutConstants {
	public static void applyDefaultStyle(String type, JFreeChart chart)
			throws UnsupportedChartTypeException {
		Plot plot = chart.getPlot();
		LegendTitle legendTitle = chart.getLegend();
		styleLegend(legendTitle);
		switch (getChartTypeConstant(type)) {
		case XY:
			styleSeries(plot);
			return;
		case PIE:
			stylePie(plot);
			return;
		case AREA_XY:
			styleXYArea(plot);
			return;
		case SCATTER:
			return;
		case AREA:
			return;
		case HORIZONTAL_BAR:
			styleBar(plot);
			return;
		case HORIZONTAL_BAR_3D:
			return;
		case LINE:
			styleSeries(plot);
			return;
		case STACKED_HORIZONTAL_BAR:
			styleBar(plot);
			return;
		case STACKED_HORIZONTAL_BAR_3D:
			return;
		case STACKED_VERTICAL_BAR:
			styleBar(plot);
			return;
		case STACKED_VERTICAL_BAR_3D:
			return;
		case VERTICAL_BAR:
			styleBar(plot);
			return;

		case VERTICAL_BAR_3D:
			return;
		case TIME_SERIES:
			styleSeries(plot);
			return;
		case CANDLE_STICK:
			return;
		case HIGH_LOW:
			return;
		case GANTT:
			styleBar(plot);
			return;
		case WIND:

			return;
		case VERTICAL_XY_BAR:
			styleBar(plot);
			return;
		case PIE_3D:
			stylePie(plot);
			return;
		case METER:

			return;
		case DIAL:

			return;
		case THERMOMETER:

			return;
		case COMPASS:

			return;
		case STACKED_AREA:
			return;
		case BUBBLE:
			styleBubbles(plot);
			return;
		case SPLINE:

			return;
		case HISTOGRAM:

			return;
		case SPIDERWEB:

			return;
		case WAFER:

			return;
		case HEATMAP:

			return;
		default:
			throw new UnsupportedChartTypeException(type + " is not supported.");
		}

	}

	public static void applyOverlaidStyle(String type, JFreeChart chart)
			throws UnsupportedChartTypeException {
		Plot plot = chart.getPlot();
		LegendTitle legendTitle = chart.getLegend();
		switch (getChartTypeConstant(type)) {

		case OVERLAY_XY:
			styleLegend(legendTitle);
			styleSeries(plot);
			styleBar(plot);
			styleXYArea(plot);
			return;
		case OVERLAY_CATEGORY:
			styleLegend(legendTitle);
			styleSeries(plot);
			styleBar(plot);
			styleXYArea(plot);
			return;
		default:
			throw new UnsupportedChartTypeException(type + " is not supported.");
		}

	}

	public static void applyCombineStyle(String type, JFreeChart chart)
			throws UnsupportedChartTypeException {
		Plot plot = chart.getPlot();
		switch (getChartTypeConstant(type)) {
		case COMBINED_XY:
			styleLegend(chart.getLegend());
			List<Plot> plots = ChartUtils.getPlots(plot);
			for (Plot subPlot : plots) {
				styleSeries(subPlot);
				styleBar(subPlot);
				stylePie(subPlot);
				styleXYArea(subPlot);
			}
			return;
		default:
			throw new UnsupportedChartTypeException(type + " is not supported.");
		}

	}

	private static final int getChartTypeConstant(String type) {
		int res = ChartTypes.typeList.indexOf(type.toLowerCase());
		if (res < 0) {
			throw new RuntimeException("unsupported chart type " + type);
		}
		return res;
	}

	public static void styleSeries(Plot plot, float width) {
		if (plot instanceof XYPlot) {
			XYPlot xyPlot = (XYPlot) plot;
			for (int i = 0; i < xyPlot.getRendererCount(); i++) {
				XYItemRenderer xyItemRenderer = xyPlot.getRenderer(i);
				if (xyItemRenderer instanceof XYLineAndShapeRenderer
						|| xyItemRenderer instanceof StandardXYItemRenderer) {
					for (int j = 0; j < xyPlot.getSeriesCount(); j++) {
						xyItemRenderer.setSeriesStroke(j, new BasicStroke(width,
								BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER));
					}
				}
			}
		}
		else if (plot instanceof CategoryPlot) {
			CategoryPlot categoryPlot = (CategoryPlot) plot;
			for (int i = 0; i < categoryPlot.getRendererCount(); i++) {
				CategoryItemRenderer itemRenderer = categoryPlot.getRenderer();
				int categoryCount = categoryPlot.getCategories().size();
				if (itemRenderer instanceof LineAndShapeRenderer) {
					LineAndShapeRenderer lineAndShapeRenderer = (LineAndShapeRenderer) itemRenderer;
					for (int j = 0; j < categoryCount; j++) {
						lineAndShapeRenderer.setSeriesStroke(j, new BasicStroke(width,
								BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER));
					}
				}
			}
		}
	}

	private static void styleSeries(Plot plot) {
		float width = PropReader.instance.getFloat(StyleConstants.SERIES_WIDTH);
		styleSeries(plot, width);
	}

	private static void styleLegend(LegendTitle legendTitle) {
		if (legendTitle != null) {
			Color borderColor = PropReader.instance.getColor(StyleConstants.LEGEND_BORDER_COLOR);
			BlockBorder border = new BlockBorder(0.5D, 0.5D, 0.5D, 0.5D, borderColor);
			legendTitle.setFrame(border);

			Color backgroundColor = PropReader.instance
					.getColor(StyleConstants.LEGEND_BACKGROUND_COLOR);
			legendTitle.setBackgroundPaint(backgroundColor);
		}
	}

	private static void styleXYArea(Plot plot) {
		if (plot instanceof XYPlot) {
			XYPlot xyPlot = (XYPlot) plot;
			for (int i = 0; i < xyPlot.getRendererCount(); i++) {
				XYItemRenderer xyItemRenderer = xyPlot.getRenderer(i);
				if (xyItemRenderer instanceof XYAreaRenderer) {
					XYAreaRenderer xyAreaRenderer = (XYAreaRenderer) xyItemRenderer;
					xyAreaRenderer.setOutline(true);
				}
			}
		}
	}

	private static void styleBar(Plot plot) {
		String style = PropReader.instance.getString(StyleConstants.XYBAR_STYLE);
		if (plot instanceof XYPlot) {
			XYPlot xyPlot = (XYPlot) plot;
			for (int i = 0; i < xyPlot.getRendererCount(); i++) {
				XYItemRenderer xyItemRenderer = xyPlot.getRenderer(i);
				if (xyItemRenderer instanceof XYBarRenderer) {
					XYBarRenderer xyBarRender = (XYBarRenderer) xyItemRenderer;
					if ("base".equalsIgnoreCase(style)) {
						xyBarRender.setBarPainter(new StandardXYBarPainter());
						xyBarRender.setBaseOutlinePaint(Color.WHITE);
						xyBarRender.setDrawBarOutline(true);
					}
				}
			}
			if (xyPlot.getDomainAxis() instanceof DateAxis) {
				DateAxis dateAxis = (DateAxis) xyPlot.getDomainAxis();
				dateAxis.setAutoRange(true);
				dateAxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
			}
		}
		else if (plot instanceof CategoryPlot) {
			CategoryPlot categoryPlot = (CategoryPlot) plot;
			for (int i = 0; i < categoryPlot.getRendererCount(); i++) {
				CategoryItemRenderer categoryItemRenderer = categoryPlot.getRenderer(i);
				if (categoryItemRenderer instanceof BarRenderer) {
					BarRenderer barRender = (BarRenderer) categoryItemRenderer;
					if ("base".equalsIgnoreCase(style)) {
						barRender.setBarPainter(new StandardBarPainter());
						barRender.setBaseOutlinePaint(Color.WHITE);
						barRender.setDrawBarOutline(true);
					}
				}
			}
		}
	}

	private static void styleBubbles(Plot plot) {
		if (plot instanceof XYPlot) {
			XYPlot xyPlot = (XYPlot) plot;
			XYItemRenderer xyItemRenderer = xyPlot.getRenderer();
			if (xyItemRenderer instanceof XYBubbleRenderer) {
				XYBubbleRenderer xyBubbleRenderer = (XYBubbleRenderer) xyItemRenderer;
				xyBubbleRenderer.setBaseOutlinePaint(Color.WHITE);

			}
		}
	}

	private static void stylePie(Plot plot) {
		if (plot instanceof PiePlot) {
			PiePlot piePlot = (PiePlot) plot;
			String labelLinkStyle = PropReader.instance
					.getString(StyleConstants.PIE_LABELLINK_STYLE);
			if ("cubic".equalsIgnoreCase(labelLinkStyle)) {
				piePlot.setLabelLinkStyle(PieLabelLinkStyle.CUBIC_CURVE);
			}
			else if ("quad".equalsIgnoreCase(labelLinkStyle)) {
				piePlot.setLabelLinkStyle(PieLabelLinkStyle.QUAD_CURVE);
			}
			else {
				piePlot.setLabelLinkStyle(PieLabelLinkStyle.STANDARD);
			}
			Color labelLinkPaint = PropReader.instance.getColor(StyleConstants.PIE_LABELLINK_COLOR);
			piePlot.setLabelLinkPaint(labelLinkPaint);
			Color labelBackgroundColor = PropReader.instance
					.getColor(StyleConstants.PIE_LABEL_BACKGROUND_COLOR);
			piePlot.setLabelBackgroundPaint(labelBackgroundColor);

			Color labelOutlineColor = PropReader.instance
					.getColor(StyleConstants.PIE_LABEL_OUTLINE_COLOR);
			piePlot.setLabelOutlinePaint(labelOutlineColor);
			Color labelShadowColor = PropReader.instance
					.getColor(StyleConstants.PIE_LABEL_SHADOW_COLOR);
			piePlot.setLabelShadowPaint(labelShadowColor);
			piePlot.setShadowXOffset(0);
			piePlot.setShadowYOffset(0);
			piePlot.setCircular(true);
			Color sectionOutlineColor = PropReader.instance
					.getColor(StyleConstants.PIE_SECTION_OUTLINE_COLOR);
			piePlot.setSectionOutlinePaint(sectionOutlineColor);
		}
	}

}
